package com.youku.util;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.Header;
import org.apache.http.HeaderIterator;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.StatusLine;
import org.apache.http.client.CookieStore;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicHeader;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.youku.exception.NetworkException;

@SuppressWarnings("unchecked")
public class HttpHelper {

	private static final Logger logger = LoggerFactory.getLogger(HttpHelper.class);

	private static final CloseableHttpClient httpClient;

	static {
		httpClient = getHttpClient();
	}

	private static final CloseableHttpClient getHttpClient() {
		PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager();
		// Increase max total connection to 200
		cm.setMaxTotal(200);
		// Increase default max connection per route to 20
		cm.setDefaultMaxPerRoute(20);
		// Increase max connections for localhost:80 to 50
		HttpHost localhost = new HttpHost("locahost", 80);
		cm.setMaxPerRoute(new HttpRoute(localhost), 50);

		return HttpClients.custom().setConnectionManager(cm).build();
	}

	public static final Map<String, Object> post(Map<String, Object> params) {
		logger.debug("发送post请求");
		CloseableHttpResponse response = null;
		try {
			String u = generateUrl(params);
			HttpPost post = new HttpPost(u);

			HttpEntity entity = generateEntity(params);
			if (entity != null)
				post.setEntity(entity);

			Header[] headers = generateHead(params);
			if (headers != null && headers.length > 0)
				post.setHeaders(headers);

			response = httpClient.execute(post, generateCookieContext(params));

			return converstResponseToMap(response);

		} catch (IOException e) {
			throw new NetworkException("进行post请求失败", e);
		} finally {
			close(response);
		}

	}

	private static HttpEntity generateEntity(Map<String, Object> params) {
		try {
			// 如果body中有数据，那么就使用这个数据作为请求体数据。如果body中没有数据而param中有数据，那么使用param中数据作为请求体数据；如果都没有数据，那么表示不生成请求体信息
			String body = (String) params.get(Consts.BODY);
			// application/x-www-form-urlencoded
			if (body == null) {
				Map<String, String> keyValuePairs = (Map<String, String>) params.get(Consts.PARAM);
				if (keyValuePairs.isEmpty()) {
					return null;
				} else {
					List<NameValuePair> pairs = new LinkedList<>();
					for (Entry<String, String> entry : keyValuePairs.entrySet()) {
						pairs.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
					}
					return new UrlEncodedFormEntity(pairs,Consts.DEFAULT_ENCODING);
				}

			} else {
				return new StringEntity(body, ContentType.create("application/json", Consts.DEFAULT_ENCODING));
			}
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException(e);
		}
	}

	private static final HttpClientContext generateCookieContext(Map<String, Object> params) {
		HttpClientContext context = HttpClientContext.create();
		CookieStore cs = context.getCookieStore();
		Map<String, String> cookieMap = (Map<String, String>) params.get(Consts.COOKIE);
		for (Entry<String, String> entry : cookieMap.entrySet()) {
			cs.addCookie(new BasicClientCookie(entry.getKey(), entry.getValue()));
		}

		context.setCookieStore(cs);

		return context;
	}

	private static final Header[] generateHead(Map<String, Object> params) {
		Map<String, String> headerMap = (Map<String, String>) params.get("header");
		if (headerMap == null)
			return null;

		Header[] headers = new Header[headerMap.size()];

		int i = 0;
		for (Entry<String, String> entry : headerMap.entrySet()) {
			headers[i++] = new BasicHeader(entry.getKey(), entry.getValue());
		}

		return headers;
	}

	private static final String generateUrl(Map<String, Object> params) {
		return (String) params.get(Consts.URL);
	}

	private static final void close(CloseableHttpResponse response) {
		logger.debug("关闭response信息");
		if (response != null) {
			try {
				EntityUtils.consume(response.getEntity());
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	public static final void close() {
		logger.debug("关闭httpclient");
		if (httpClient != null)
			try {
				httpClient.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
	}

	private static final Map<String, Object> converstResponseToMap(CloseableHttpResponse response) {
		Map<String, Object> result = new HashMap<>();
		StatusLine status = response.getStatusLine();

		result.put("status", status.getStatusCode());
		result.put("body", getResponseBody(response));

		Map<String, String> cookieMap = null;

		HeaderIterator it = response.headerIterator("Set-Cookie");

		while (it.hasNext()) {
			if (cookieMap == null) {
				cookieMap = new HashMap<>();
			}

			Header element = it.nextHeader();

			String[] v = element.getValue().split(";");
			for (String s : v) {
				if (s != null && !"".equals(s)) {
					String[] arr = s.split("=");

					if (arr.length < 2) {
						continue;
					}

					cookieMap.put(arr[0], arr[1]);
				}
			}
		}

		if (cookieMap == null)
			cookieMap = Collections.emptyMap();

		result.put("cookie", cookieMap);

		return result;

	}

	private static String getResponseBody(CloseableHttpResponse response) {
		HttpEntity entity = response.getEntity();
		try {
			return entity == null ? null : EntityUtils.toString(entity);
		} catch (ParseException | IOException e) {
			throw new RuntimeException(e);
		}
	}
}
